package com.xiezc;

import java.security.SecureRandom;

/**
 * id生成算法
 *
 * @author wb-xzc291800
 */
public class Snowflake {
    /**
     * 基准时间
     */
    private long twepoch = 1288834974657L;
    /**
     * Thu, 04 Nov 2010 01:42:54 GMT
     * 区域标志位数
     */
    private final static long regionIdBits = 3L;
    /**
     * 机器标识位数
     */
    private final static long workerIdBits = 10L;
    /**
     * 序列号识位数
     */
    private final static long sequenceBits = 10L;
    /**
     * 区域标志ID最大值
     */
    private final static long maxRegionId = -1L ^ (-1L << regionIdBits);
    /**
     * 机器ID最大值
     */
    private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);
    /**
     * 序列号ID最大值
     */
    private final static long sequenceMask = -1L ^ (-1L << sequenceBits);
    /**
     * 机器ID偏左移10位
     */
    private final static long workerIdShift = sequenceBits;
    /**
     * 业务ID偏左移20位
     */
    private final static long regionIdShift = sequenceBits + workerIdBits;
    /**
     * 时间毫秒左移23位
     */
    private final static long timestampLeftShift = sequenceBits + workerIdBits + regionIdBits;

    private static long lastTimestamp = -1L;
    private long sequence = 0L;
    private final long workerId;
    private final long regionId;

    public Snowflake(long workerId, long regionId) {
        // 如果超出范围就抛出异常
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException("worker Id can't be greater than %d or less than 0");
        }
        if (regionId > maxRegionId || regionId < 0) {
            throw new IllegalArgumentException("datacenter Id can't be greater than %d or less than 0");
        }
        this.workerId = workerId;
        this.regionId = regionId;
    }

    public Snowflake(long workerId) {
        // 如果超出范围就抛出异常
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException("worker Id can't be greater than %d or less than 0");
        }
        this.workerId = workerId;
        this.regionId = 0;
    }

    public long generate() {
        return this.nextId(false, 0);
    }

    private synchronized long nextId(boolean isPadding, long busId) {
        long timestamp = timeGen();
        long paddingnum = regionId;
        if (isPadding) { paddingnum = busId; }
        if (timestamp < lastTimestamp) {
            try {
                throw new Exception("Clock moved backwards. Refusing to generate id for " + (lastTimestamp - timestamp)
                    + " milliseconds");
            } catch (Exception e) { e.printStackTrace(); }
        }
        //如果上次生成时间和当前时间相同,在同一毫秒内
        if (lastTimestamp == timestamp) {
            //sequence自增，因为sequence只有10bit，所以和sequenceMask相与一下，去掉高位
            sequence = (sequence + 1) & sequenceMask;
            //判断是否溢出,也就是每毫秒内超过1024，当为1024时，与sequenceMask相与，sequence就等于0
            if (sequence == 0) {
                //自旋等待到下一毫秒
                timestamp = tailNextMillis(lastTimestamp);
            }
        } else {
            // 如果和上次生成时间不同,重置sequence，就是下一毫秒开始，sequence计数重新从0开始累加,
            // 为了保证尾数随机性更大一些,最后一位设置一个随机数
            sequence = new SecureRandom().nextInt(10);
        }
        lastTimestamp = timestamp;
        return ((timestamp - twepoch) << timestampLeftShift) | (paddingnum << regionIdShift) | (workerId
            << workerIdShift) | sequence;
    }

    private long tailNextMillis(final long lastTimestamp) {
        long timestamp = this.timeGen();
        while (timestamp <= lastTimestamp) { timestamp = this.timeGen(); }
        return timestamp;
    }

    protected long timeGen() {
        return System.currentTimeMillis();
    }

}



